using System;
using System.Collections.Generic;
using System.Text;
using ElteIk.RoboSoccer.Core;
using ElteIk.RoboSoccer.Core.Physics;
using ElteIk.RoboSoccer.Communication;


namespace ElteIk.RoboSoccer.Simulator
{
    public class RoboSoccerSimulator
    {
        private RoboSoccerGame game;
        public RoboSoccerGame Game { get { return game; } }

        private MultithreadedServer server;
        private TeamObjectives teamObjectives;

        public event EventHandler SimulationFinished;

        public RoboSoccerSimulator(ITactics leftTactics, ITactics rightTactics)
        {
            game = new Core.RoboSoccerGame();
            game.Initialize();

            server = new MultithreadedServer(game);
            server.AddClient(leftTactics, 1);
            server.AddClient(rightTactics, 2);

            game.Updated += new EventHandler(UpdateHandler);
            game.TimeOut += new EventHandler(GameEndHandler);
        }


        /// <summary>
        /// Simulation start with normal game. 
        /// </summary>
        /// <param name="startingState"></param>
        /// <param name="teamObjectives"></param>
        /// <param name="maxTime"></param>
        public void Start(StartingState startingState, TeamObjectives teamObjectives, decimal maxTime)
        {
            this.teamObjectives = teamObjectives;
            game.DoPlacements();
            SetStartingState(startingState);
            game.SynchronizePhysics();
            game.TimeLimit = maxTime;
            game.Start();   
        }


        /// <summary>
        /// Simulation start in quick mode. You can't use the game object during the simulation (for example for displaying it).
        /// You should wait for SimulationFinished event!
        /// </summary>
        /// <param name="startingState"></param>
        /// <param name="teamObjectives"></param>
        /// <param name="maxTime"></param>
        public void QuickStart(StartingState startingState, TeamObjectives teamObjectives, decimal maxTime)
        {
            this.teamObjectives = teamObjectives;
            game.DoPlacements();
            SetStartingState(startingState);
            game.Run(maxTime);
        }


        private void SetStartingState(StartingState startingState)
        {
            if (startingState.BallState.IsSet)
            {
                game.Ball.Position = startingState.BallState.Position;
                game.Ball.Velocity = startingState.BallState.Velocity;
            }

            for(int i=0;i<startingState.LeftTeamState.PlayerStateList.Count;++i)
            {
                PlayerState playerState = startingState.LeftTeamState.PlayerStateList[i];
                if (playerState.IsSet)
                {
                    game.LeftTeam.Robots[i].Position = playerState.Position;
                    game.LeftTeam.Robots[i].Heading = playerState.Heading;
                }
            }

            for (int i = 0; i < startingState.RightTeamState.PlayerStateList.Count; ++i)
            {
                PlayerState playerState = startingState.RightTeamState.PlayerStateList[i];
                if (playerState.IsSet)
                {
                    game.RightTeam.Robots[i].Position = playerState.Position;
                    game.RightTeam.Robots[i].Heading = playerState.Heading;
                }
            }
        }

        private void UpdateHandler(Object sender, EventArgs e)
        {
            // if all of the objectives are ready
            if (teamObjectives.IsReady())
            {
                game.Stop();
            }
        }


        private void GameEndHandler(Object sender, EventArgs e)
        {
            OnSimulationFinished();
        }

        private void OnSimulationFinished()
        {
            if (SimulationFinished != null)
            { SimulationFinished(this, new EventArgs()); }
        }

        public TeamObjectives GetTeamObjectives()
        {
            return teamObjectives;
        }

        public decimal GetElapsedTime()
        {
            return game.Time;
        }
    }
}
